import network
from webservice import socketservice
from machine import Pin
from utime import ticks_ms, sleep_ms
from drivers import hc_sr04
from drivers import myrelay
from others import PinS
#from timercontroller import TimerController
import timercontroller
from micropython import alloc_emergency_exception_buf
import gc

alloc_emergency_exception_buf(100)

u"""
定义常量
"""

# 定义服务器端口
PORT = 8266

# 定义按钮响应时间间隔，在此间隔内多次按按钮无效，用于消除抖动，单位：毫秒
BUTTON_RESPONSE_INTERVAL = 100



u"""
定义对象
"""

# 定义设备 wifi 为 ap 模式
access_point = network.WLAN(network.AP_IF)

# 定义设备板载 led，设置为输出模式，赋初值 1
# 板载 led（GPIO2）自带上拉电阻为高电平，即低电平点亮，赋初值为 1 代表熄灭 led
led = Pin(PinS.GPIO.LED, Pin.OUT, value = 1)

# 定义按钮，设置为开漏模式（用于输入）并拉高电平，高电平代表按钮 未 按下
button = Pin(PinS.GPIO.GPIO12, Pin.OPEN_DRAIN, Pin.PULL_UP)

# 定义超声波测距模块并初始化
# GPIO5 为 trig 针脚，GPIO4 为 echo 针脚
hcsr = hc_sr04.HC_SR04()

relay = myrelay.Relay()

timer_controller = timercontroller.TimerController(relay, hcsr)

gc.collect()


def init_access_point(essid = "", password = "12345678", hidden = False):
	u"""
	初始化设备热点
	:param essid: 热点名称
	:param password: 热点密码
	:param hidden: 是否隐藏热点名称
	:return: 无
	"""

	# 启动热点
	access_point.active(True)

	# 如果调用函数未指定 essid，则 essid 为 MicroPy- 加设备 mac 地址后6位
	if (essid == ""):
		mac = access_point.config("mac")
		essid = "MicroPy-%02x%02x%02x" % (mac[-3], mac[-2], mac[-1])

	# 设置热点相关信息
	access_point.config(essid = essid, password = password, hidden = hidden)

	print("\n\nWifi access point intialized:\n    ssid: %s\n    password: %s" % (essid, password))

def init_button_response():
	u"""
	初始化按钮
	:return: 无
	"""

	# 按钮使用中断处理下降沿检测（falling），由回调函数 button_response_cb 响应按钮中断事件
	button.irq(button_response_cb, Pin.IRQ_FALLING)

u"""
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针（地址）作为参数传递给另一个函数，
当这个指针被用来调用其所指向的函数时，我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用，
而是在特定的事件或条件发生时由另外的一方调用的，
用于对该事件或条件进行响应。
	--- 解释来自 百度百科
"""



# 定义上一次按钮中断时间，单位：毫秒
button_response_last_time = ticks_ms()

def button_response_cb(response):
	u"""
	按钮按下事件响应回调函数

	按钮下降沿检测事件可能会在短时间内多次触发，产生非预期的效果，即 抖动
	要消除抖动，只需要在规定时间内仅响应一次事件即可
	:param response: 响应对象，即 button 按钮，由 button.irq() 函数传递
	:return: 无
	"""

	# 声明 上一次按钮中断时间 在本函数中为全局变量
	global button_response_last_time, relay

	# 去掉下边语句的注释符号，就可以观察到按钮按下时本函数被多次调用
	# print("button responsed after ", ticks_ms() - button_response_last_time)

	# 确定函数前后两次调用时间是否超过规定时间（100毫秒）
	if ((ticks_ms() - button_response_last_time) > BUTTON_RESPONSE_INTERVAL):
		# 确定按钮当前状态是否为低电平，低电平代表按钮被按下
		if (response.value() == 0):
			# 根据继电器当前状态，选择闭合或断开继电器
			relay.active(myrelay.Relay.ACTIVE_TIME_INFINITE) if not relay.isActived else relay.deactive()

			# 上边的三元表达式等同于如下代码：
			# if (not relay.isActived()):
			#     relay.active(Relay.ACTIVE_TIME_INFINITE)
			# else:
			#     relay.deactive()

			sleep_ms(50)

	# 更新 上一次按钮中断时间 为当前时间
	button_response_last_time = ticks_ms()

def check_wifi_status_cb(timer):
	global timer_controller

	if (access_point.isconnected()):
		timer_controller.stop_led_blink_timer()

		if (led.value() == 0):
			stop_led_blink()
	else:
		start_led_blink()



def start_led_blink():
	global timer_controller

	timer_controller.start_led_blink_timer(led)

def stop_led_blink():
	led.on()



u"""
主函数
"""
if (__name__ == "__main__"):
	# 定义继电器模块并初始化为低电平激励模式
	# GPIO14 控制激励针脚
	relay.init(PinS.GPIO.GPIO14, myrelay.Relay.ACTIVE_MODE_LOW)

	hcsr.init(PinS.GPIO.GPIO5, PinS.GPIO.GPIO4)

	# 调用函数初始化设备热点
	# essid 为 Walkline，密码使用默认的 12345678
	init_access_point("Walkline")

	# 调用函数初始化按钮
	init_button_response()

	# 调用函数初始化服务器
	socketservice = socketservice.SocketService(timer_controller)
	socketservice.init()

	# 启动获取距离信息定时器
	# timercontroller.start_distance_info_timer(get_distance_info_cb)

	# 启动检查 wifi 连接状态定时器
	timer_controller.start_wifi_status_timer(check_wifi_status_cb)

	timer_controller.start_client_request_timer(socketservice.check_client_request_cb)
